package com.easy.core.dbHandle.imp;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.apache.commons.lang.SerializationUtils;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.util.Assert;

import com.easy.core.dbHandle.BasicDao;
import com.easy.core.dbHandle.PaginationSupport;

/**
 * <p>Description: </p>
 * <p>CreateDate:@2013-8-23</p>
 * @author zhangj
 */
@SuppressWarnings({"unchecked"})
public class BasicDaoImp<T, PK extends Serializable> implements BasicDao<T, PK> {
	
	protected final Logger log = Logger.getLogger(this.getClass());
	private SessionFactory sessionFactory;
	
	public T findById(final PK id){
		Assert.notNull(id);
		T entity = (T) this.sessionFactory.getCurrentSession().get(getClazz(0), id);
		if (log.isDebugEnabled() && entity == null)
			log.debug("Uh oh, '" + getClazz(0) + "' object with id '" + id + "' not found...");
		return entity;
//		Assert.notNull(id);
//		T entity = (T) this.getHibernateTemplate().get(getClazz(0), id);
//		if (log.isDebugEnabled() && entity == null)
//			log.debug("Uh oh, '" + getClazz(0) + "' object with id '" + id + "' not found...");
//		return entity;
	}
	
	public <K> Object findById(Class<K> clazz, PK id) {
//		T entity = (T) this.getHibernateTemplate().get(clazz, id);
		T entity = (T)this.getSessionFactory().getCurrentSession().get(clazz, id);
		if(log.isDebugEnabled() && null == entity) {
			log.debug("'" + clazz + "' object with id '" + id + "' not found...");
		}
		return entity;
	}
	
	public <K> Object findById(String entityName, PK id) {
//		T entity = (T) this.getHibernateTemplate().get(entityName, id);
		T entity = (T)this.sessionFactory.getCurrentSession().get(entityName, id);
		if (log.isDebugEnabled() && entity == null)
			log.debug("'" + entityName + "' object with id '" + id + "' not found...");
		return entity;
	}

	public List<T> findAll() {
		return findByCriteria(DetachedCriteria.forClass(getClazz(0)));
	}
	
	public <K> List<K> findAll(final Class<K> clazz) {
		return this.sessionFactory.getCurrentSession().createCriteria(clazz).list();
//		return this.getHibernateTemplate().executeFind(new HibernateCallback() {
//			@Override
//			public Object doInHibernate(Session session) throws HibernateException, SQLException {
//				DetachedCriteria dc = DetachedCriteria.forClass(clazz);
//				return dc.getExecutableCriteria(session).list();
//			}
//		});
	}
	
	public <K> List<K> findAll(final String entityName) {
		Criteria query = this.sessionFactory.getCurrentSession().createCriteria(entityName);
		return query.list();
//		return this.getHibernateTemplate().executeFind(new HibernateCallback() {
//			@Override
//			public Object doInHibernate(Session session) throws HibernateException, SQLException {
//				DetachedCriteria dc = DetachedCriteria.forEntityName(entityName);
//				return dc.getExecutableCriteria(session).list();
//			}
//		});
	}

	public List<T> findByCriteria(final DetachedCriteria dc) {
		return dc.getExecutableCriteria(this.sessionFactory.getCurrentSession()).list();
//		return (List<T>) this.getHibernateTemplate().execute(new HibernateCallback() {
//			@Override
//			public Object doInHibernate(Session session) throws HibernateException, SQLException {
//				return dc.getExecutableCriteria(session).list();
//			}
//		});
	}
	
	public List<T> findTopByCriteria(final DetachedCriteria dc, final int num){
		Criteria criteria = dc.getExecutableCriteria(this.sessionFactory.getCurrentSession());
		return criteria.setFirstResult(0).setMaxResults(num).list();
//		return this.getHibernateTemplate().executeFind(new HibernateCallback() {
//			@Override
//			public Object doInHibernate(Session session) throws HibernateException, SQLException {
//				Criteria criteria = dc.getExecutableCriteria(session);
//				return criteria.setFirstResult(0).setMaxResults(num).list();
//			}
//		});
	}
	
	public Long findCountByCriteria(final DetachedCriteria dc){
		Criteria criteria = dc.getExecutableCriteria(sessionFactory.getCurrentSession());
		Object obj = criteria.setProjection(Projections.rowCount()).uniqueResult();
		return ((Integer)obj).longValue();
//		Object obj = this.getHibernateTemplate().execute(new HibernateCallback() {
//			@Override
//			public Object doInHibernate(Session session) throws HibernateException, SQLException {
//				Criteria criteria = dc.getExecutableCriteria(session);
//				return criteria.setProjection(Projections.rowCount()).uniqueResult();
//			}
//		});
//		return ((Integer)obj).longValue();
	}
	
	public PaginationSupport<T> findPageByCriteria(final DetachedCriteria dc, final int pageSize, final int startIndex, final List<Order> orderList){
		DetachedCriteria clone = (DetachedCriteria) SerializationUtils.clone(dc);
		Criteria c = clone.getExecutableCriteria(this.sessionFactory.getCurrentSession());
		int totalCount = ((Integer) c.setProjection(Projections.rowCount()).uniqueResult()).intValue();
		if(null != orderList) for (Order order : orderList) {
			dc.addOrder(order);
		}
		Criteria criteria = dc.getExecutableCriteria(this.sessionFactory.getCurrentSession());
		List<T> items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
		clone.setProjection(null);
		PaginationSupport<T> ps = new PaginationSupport<T>(items, totalCount, pageSize, startIndex);
		return ps;
//		return (PaginationSupport<T>) this.getHibernateTemplate().execute(new HibernateCallback() {
//			@Override
//			public Object doInHibernate(Session session) throws HibernateException, SQLException {
//				DetachedCriteria clone = (DetachedCriteria) SerializationUtils.clone(dc);
//				Criteria c = clone.getExecutableCriteria(session);
//				int totalCount = ((Integer) c.setProjection(Projections.rowCount()).uniqueResult()).intValue();
//				if(null != orderList) for (Order order : orderList) {
//					dc.addOrder(order);
//				}
//				Criteria criteria = dc.getExecutableCriteria(session);
//				List<T> items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
//				clone.setProjection(null);
//				PaginationSupport<T> ps = new PaginationSupport<T>(items, totalCount, pageSize, startIndex);
//				return ps;
//			}
//		});
	}
	
	public List<?> findByHQL(final String hql, final Object... params){
		Query query = this.sessionFactory.getCurrentSession().createQuery(hql);
		for(int i=0;i<params.length;i++){
			query.setParameter(i, params[i]);
		}
		return query.list();
//		return getHibernateTemplate().executeFind(new HibernateCallback() {
//			public Object doInHibernate(Session session) throws HibernateException, SQLException {
//				Query query = session.createQuery(hql);
//				for(int i=0;i<params.length;i++){
//					query.setParameter(i, params[i]);
//				}
//				return query.list();
//			}
//		});
	}
	
	public PK save(T object) {
		Assert.notNull(object);
		return (PK) this.sessionFactory.getCurrentSession().save(object);
//		Assert.notNull(object);
//		return (PK) this.getHibernateTemplate().save(object);
	}
	
	public PK save(String entityName, T object) {
		Assert.notNull(object);
		return (PK) this.sessionFactory.getCurrentSession().save(entityName, object);
//		return (PK) this.getHibernateTemplate().save(entityName, object);
	}

	public void update(T object) {
		Assert.notNull(object);
		this.sessionFactory.getCurrentSession().update(object);
//		this.getHibernateTemplate().update(object);
	}
	
	public void update(String entityName, T object) {
		Assert.notNull(object);
		this.sessionFactory.getCurrentSession().update(entityName, object);
//		this.getHibernateTemplate().update(entityName, object);
	}

	public void delete(T object) {
		Assert.notNull(object);
		this.sessionFactory.getCurrentSession().delete(object);
//		this.getHibernateTemplate().delete(object);
	}

	public Session getCurrentSession(boolean allowCreate) {
		return this.sessionFactory.getCurrentSession();
//		return this.getSession(allowCreate);
	}

	private Class<?> getClazz(int Serializable){
		return (Class<?>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[Serializable];
	}

	public SessionFactory getSessionFactory() {
		return this.sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
}
